use serde::Deserialize;
use std::collections::HashSet;

fn main(){
    // 找到Lesche.toml所在目录
    let work_dir = get_work_dir();

    // 读取并解析Lesche.toml内容
    let config = get_config(&work_dir);

    // 检查是否有重复的shader.stage
    let mut outs: HashSet<String> = HashSet::with_capacity(config.shader.len() as usize);
    for shader in &config.shader {
        if !outs.insert(shader.stage.clone()) {
            println!("\n重复的shader.stage：{}\n", shader.stage);
            return;
        }
    }//for

    // target目录

    let mut target = work_dir.clone();
    target.push_str(&config.target);
    target.push('/');
    check_target(&target);

    // 逐个编译shader项目

    for shader in &config.shader {
        // 从分离的多个文件中获取代码
        let files = get_files(&work_dir, &shader);
        let code = get_code(&config.version, files);

        let mut code_file = target.clone();
        code_file.push_str(&shader.stage);

        // 将代码写入到一个文件中
        std::fs::write(&code_file, &code).unwrap();

        // 编译代码
        compile(&code_file, &shader.stage);
    }//for
}//main

/*********************************************************
    Deserialize Structs
**********************************************************/

#[derive(Deserialize)]
struct Config {
    target: String,
    version: String,
    shader: Vec<Shader>
}

#[derive(Deserialize)]
struct Shader {
    stage: String,
    files: Vec<String>
}

/*********************************************************
    Helper Functions
**********************************************************/

fn get_work_dir() ->String {
    let mut path = String::new();
    loop {
        let found = std::fs::read_dir(&path)
            .expect("\n找不到Lesche.toml文件\n\n")
            .any(|d| {
            let d = d.unwrap();

            let file_name = String::from("Lesche.toml") == d
                .file_name()
                .into_string()
                .unwrap();

            let file_type = d
                .file_type()
                .unwrap()
                .is_file();

            file_name && file_type
        });

        if found {return path;}

        path.push_str("../");
    }//loop
}//fn

fn get_config(dir: &String) ->Config {
    let mut path = dir.clone();
    path.push_str("Lesche.toml");

    let config = std::fs::read_to_string(path);
    let config = config.expect("\n无法读取Lesche.toml\n\n");
    if let Ok(config) = toml::from_str::<Config>(&config) {config} else {
        panic!("\nLesche.toml文件内容格式错误\n");
    }
}//fn

fn get_files(work_dir: &String, shader: &Shader) -> Vec<String> {
    let mut files: Vec<String> = Vec::new();
    for file in &shader.files{
        let mut tmp = work_dir.clone();
        tmp.push_str(&file);
        files.push(tmp);
    }//for

    return files;
}//fn

fn get_code(version: &String, files: Vec<String>) ->String {
    let mut code = String::from("#version ");
    code.push_str(version);

    for file in files {
        code.push('\n');
        if let Ok(file_code) = std::fs::read_to_string(&file) {
            code.push_str(&file_code);
        } else { panic!("\n\n无法读取文件：{file}\n"); }
    }//for

    code
}

fn check_target(target: &String) {
    if let Err(e) = std::fs::create_dir(target) {
        if e.kind() != std::io::ErrorKind::AlreadyExists {
            panic!("\n无法创建target目录\n");
        }
    }
}

fn compile(code_file: &String, stage: &String) {
    // 调用glslangValidator命令进行编译
    let mut spv = code_file.clone();
    spv.push_str(".spv");
    let args = [
        "-V",
        "-S",
        stage,
        code_file,
        "-o",
        &spv
    ];

    std::process::Command::new("glslangValidator")
        .args(args)
        .spawn()
        .unwrap()
        .wait()
        .unwrap();
}//fn
